#ifndef NASCCL_Decode_Cpp
#define NASCCL_Decode_Cpp
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
    The Decode function is main NASCCL decryption routine.
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <Windows.H>

#include "NASCCL.H"
#include "sBoxes.H"
#include "Math.H"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool NASCCL::Decode(void *inData, void *outData, int inDataLen)
{
    NASCCLByte FirstValue     = 0;
    NASCCLByte SecondValue    = 0;
    NASCCLByte LastValue      = 0;
    NASCCLByte sValue         = 0;
    NASCCLByte KeyValue       = 0;
    NASCCLByte IncrementValue = 0;
    NASCCLByte MidOffset      = 0;
    NASCCLByte RoundByte1     = 0;
    NASCCLByte RoundByte2     = 0;

    int Box        = 0;
    int LocalLoop  = 0;
    int CharOffset = 0;

    // If NOT crypting full data set then copy MValue to the Matrix
    if( !(Attribute &CRYPTFULLDATASET))
        memcpy(&Matrix, &MValue, sizeof(MValue));

    if(!(Attribute &RUNNINGKEYPOSITION))
        KeyHashPos = 0;

    FirstValue  = Merge( Matrix[0][5], Matrix[1][4], Matrix[2][3],
                         Matrix[3][2], Matrix[4][1], Matrix[5][0] );

    SecondValue = Merge( Matrix[5][0], Matrix[4][1], Matrix[3][2],
                         Matrix[2][3], Matrix[1][5], Matrix[0][5] );

    IncrementValue = (FirstValue  + 17) &255;
    LastValue      = (SecondValue + 35) &255;
    MidOffset      = (FirstValue  + 53) &255;
    CharOffset     = (SecondValue + 71) &255;

    while( LocalLoop < inDataLen )
    {
        KeyValue  = ((NASCCLByte *)KeyHash)[KeyHashPos];
        sValue    = sDataBox[Box][KeyValue];

        if(Attribute &ENSUREOFFSET)
        {
            if( (((NASCCLByte *)inData)[LocalLoop] + IncrementValue) == ((NASCCLByte *)inData)[LocalLoop] )
                IncrementValue += CharOffset;
        }

        ((NASCCLByte *)outData)[LocalLoop] = (((NASCCLByte *)inData)[LocalLoop] - IncrementValue) &255;

        // If using Forward Matrix Mutaion Add The Output Data to the matrix values
        if(Attribute &FMATRIXMUTATION)
            MatrixMutate(((NASCCLByte *)outData)[LocalLoop]);

        // If using Reverse Matrix Mutaion Add The Input Data to the matrix values
        if(Attribute &RMATRIXMUTATION)
            MatrixMutate(((NASCCLByte *)inData)[LocalLoop]);

        // If using Cryptograpic Matrix Mutaion Add The increment value to the matrix values
        if(Attribute &CMATRIXMUTATION)
            MatrixMutate(IncrementValue);

        // Round 1 (Calculate 6 Rounds)
        Matrix[0][0] = Round1(Matrix[5][5] ,SecondValue);
        Matrix[0][1] = Round2(Matrix[5][4] ,MidOffset);
        Matrix[0][2] = Round3(Matrix[5][3] ,SecondValue);
        Matrix[0][3] = Round4(Matrix[5][2] ,MidOffset);
        Matrix[0][4] = Round5(Matrix[5][1] ,SecondValue);
        Matrix[0][5] = Round6(Matrix[5][0] ,MidOffset);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (2)
            MidOffset = Merge( Matrix[0][0], Matrix[0][1], Matrix[0][2],
                               Matrix[0][3], Matrix[0][4], Matrix[0][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (0 & 5)
        if(Attribute &SWAPMATRIX) SwapMatrix(0, 5);

        // Round 2 (Calculate 6 Rounds)
        Matrix[1][0] = Round1(Matrix[0][5] ,MidOffset);
        Matrix[1][1] = Round2(Matrix[0][4] ,LocalLoop);
        Matrix[1][2] = Round3(Matrix[0][3] ,MidOffset);
        Matrix[1][3] = Round4(Matrix[0][2] ,LocalLoop);
        Matrix[1][4] = Round5(Matrix[0][1] ,MidOffset);
        Matrix[1][5] = Round6(Matrix[0][0] ,LocalLoop);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (3)
            MidOffset = Merge( Matrix[1][0], Matrix[1][1], Matrix[1][2],
                               Matrix[1][3], Matrix[1][4], Matrix[1][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (1 & 4)
        if(Attribute &SWAPMATRIX) SwapMatrix(1, 4);

        // Round 3 (Calculate 6 Rounds)
        Matrix[2][0] = Round1(Matrix[1][5] ,KeyValue);
        Matrix[2][1] = Round2(Matrix[1][4] ,MidOffset);
        Matrix[2][2] = Round3(Matrix[1][3] ,KeyValue);
        Matrix[2][3] = Round4(Matrix[1][2] ,MidOffset);
        Matrix[2][4] = Round5(Matrix[1][1] ,KeyValue);
        Matrix[2][5] = Round6(Matrix[1][0] ,MidOffset);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (4)
            MidOffset = Merge( Matrix[2][0], Matrix[2][1], Matrix[2][2],
                               Matrix[2][3], Matrix[2][4], Matrix[2][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (2 & 3)
        if(Attribute &SWAPMATRIX) SwapMatrix(2, 3);

        // Round 4 (Calculate 6 Rounds)
        Matrix[3][0] = Round1(Matrix[2][5] ,MidOffset);
        Matrix[3][1] = Round2(Matrix[2][4] ,KeyHashPos);
        Matrix[3][2] = Round3(Matrix[2][3] ,MidOffset);
        Matrix[3][3] = Round4(Matrix[2][2] ,KeyHashPos);
        Matrix[3][4] = Round5(Matrix[2][1] ,MidOffset);
        Matrix[3][5] = Round6(Matrix[2][0] ,KeyHashPos);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (5)
            MidOffset = Merge( Matrix[3][0], Matrix[3][1], Matrix[3][2],
                               Matrix[3][3], Matrix[3][4], Matrix[3][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (3 & 2)
        if(Attribute &SWAPMATRIX) SwapMatrix(3, 2);

        // Round 5 (Calculate 6 Rounds)
        Matrix[4][0] = Round1(Matrix[3][5] ,FirstValue);
        Matrix[4][1] = Round2(Matrix[3][4] ,MidOffset);
        Matrix[4][2] = Round3(Matrix[3][3] ,FirstValue);
        Matrix[4][3] = Round4(Matrix[3][2] ,MidOffset);
        Matrix[4][4] = Round5(Matrix[3][1] ,FirstValue);
        Matrix[4][5] = Round6(Matrix[3][0] ,MidOffset);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (6)
            MidOffset = Merge( Matrix[4][0], Matrix[4][1], Matrix[4][2],
                               Matrix[4][3], Matrix[4][4], Matrix[4][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (4 & 1)
        if(Attribute &SWAPMATRIX) SwapMatrix(4, 1);

        // Round 6 (Calculate 6 Rounds)
        Matrix[5][0] = Round1(Matrix[4][5] ,MidOffset);
        Matrix[5][1] = Round2(Matrix[4][4] ,sValue);
        Matrix[5][2] = Round3(Matrix[4][3] ,MidOffset);
        Matrix[5][3] = Round4(Matrix[4][2] ,sValue);
        Matrix[5][4] = Round5(Matrix[4][1] ,MidOffset);
        Matrix[5][5] = Round6(Matrix[4][0] ,sValue);
        if(Attribute &ROUNDMUTATION) // Use MidOffset To Mutate Round (1)
            MidOffset = Merge( Matrix[5][0], Matrix[5][1], Matrix[5][2],
                               Matrix[5][3], Matrix[5][4], Matrix[5][5] );

        // If We Are Using "swap Matrix" Then Swap Matrix (5 & 0)
        if(Attribute &SWAPMATRIX) SwapMatrix(5, 0);

        // Calculate Two End Results. One Forward, One Backwards. Round The Two Values.
        if(Attribute &ROUNDRESULT)
        {
            RoundByte1 = Merge( Matrix[5][0], Matrix[5][1], Matrix[5][2],
                                Matrix[5][3], Matrix[5][4], Matrix[5][5] );

            RoundByte2 = Merge( Matrix[5][5], Matrix[5][4], Matrix[5][3],
                                Matrix[5][2], Matrix[5][1], Matrix[5][0] );

            IncrementValue = RoundBytes(RoundByte1, RoundByte2, LastValue &255);
        }
        else { // Calculate one end result.
            IncrementValue = Merge( Matrix[5][0], Matrix[5][1], Matrix[5][2],
                                Matrix[5][3], Matrix[5][4], Matrix[5][5] );
        }

        if(IncrementValue == 0)
            IncrementValue = CharOffset;

        // Ensure That There Are No Repeating Increment Values
        if(Attribute &NONREPETITION)
        {
            if(IncrementValue == LastValue)
                IncrementValue += CharOffset;

            LastValue = IncrementValue;
        }

        FirstValue += (KeyValue + IncrementValue);

        // If We Are Not Using "Round Mutation" Set MidOffset To IncrementValue.
        if( !(Attribute &ROUNDMUTATION) )
            MidOffset = IncrementValue;

        if(CharOffset == 255)
            CharOffset = 0;

        if(Box == BoxSize)
            Box = -1;

        if(KeyHashPos == KeyHashLen)
            KeyHashPos = -1;

        KeyHashPos++;
        CharOffset++;
        LocalLoop++;
        Box++;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif

